﻿namespace CodeProject.Podium.WP7
{
    using System;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.IO;
    using System.IO.IsolatedStorage;
    using Microsoft.Phone.Notification;

    public class PushNotifier : IDisposable
    {
        private HttpNotificationChannel httpChannel;

        public HttpNotificationChannel GetHttpChannel()
        {
            return httpChannel;
        }

        string channelName;
        string serviceName;
        string fileName = "PushNotificationsSettings.dat";

        const int pushConnectTimeout = 30;

        public PushNotifier(string channelName, string serviceName)
        {
            this.channelName = channelName;
            this.serviceName = serviceName;
        }

        private void Trace(string message)
        {
#if DEBUG
            Debug.WriteLine(message);
#endif
        }

        private void DoConnect()
        {
            try
            {
                httpChannel = HttpNotificationChannel.Find(channelName);

                if (null != httpChannel)
                {
                    Trace("Channel Exists - no need to create a new one");
                    SubscribeToChannelEvents();

                    Trace("Register the URI with 3rd party web service");
                    SubscribeToService();

                    Trace("Subscribe to the channel to Tile and Toast notifications");
                    SubscribeToNotifications();

                    Trace("Channel recovered");
                }
                else
                {
                    Trace("Trying to create a new channel...");
                    httpChannel = new HttpNotificationChannel(channelName, serviceName);
                    Trace("New Push Notification channel created successfully");

                    SubscribeToChannelEvents();

                    Trace("Trying to open the channel");
                    httpChannel.Open();
                    Trace("Channel open requested");
                }
            }
            catch (Exception ex)
            {
                Trace("Channel error: " + ex.Message);
            }
        }

        private void SubscribeToChannelEvents()
        {            
            httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated);
            httpChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(httpChannel_HttpNotificationReceived);
            httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(httpChannel_ExceptionOccurred);
            httpChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(httpChannel_ShellToastNotificationReceived);
        }

        public event EventHandler Subscribed;

        private void SubscribeToService()
        {
            if (Subscribed != null)
                Subscribed(this, EventArgs.Empty);
        }

        private void SubscribeToNotifications()
        {
            try
            {
                if (httpChannel.IsShellToastBound == true)
                {
                    Trace("Already bounded (register) to to Toast notification");
                }
                else
                {
                    Trace("Registering to Toast Notifications");
                    httpChannel.BindToShellToast();
                }
            }
            catch (Exception ex)
            {
                Trace(ex.ToString());
            }

            try
            {
                if (httpChannel.IsShellTileBound == true)
                {
                    Trace("Already bounded (register) to Tile Notifications");
                }
                else
                {
                    Trace("Registering to Tile Notifications");

                    Collection<Uri> uris = new Collection<Uri>();
                    uris.Add(new Uri("http://jquery.andreaseberhard.de/pngFix/pngtest.png"));

                    httpChannel.BindToShellTile(uris);
                }
            }
            catch (Exception ex)
            {
                Trace(ex.ToString());
            }
        }

        void httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
        {
            Trace("Channel opened. Got Uri:\n" + httpChannel.ChannelUri.ToString());
            SaveChannelInfo();

            Trace("Subscribing to channel events");
            SubscribeToService();
            SubscribeToNotifications();

            Trace("Channel created successfully");
        }

        void httpChannel_ExceptionOccurred(object sender, NotificationChannelErrorEventArgs e)
        {
            Trace(e.ErrorType + " occurred: " + e.Message);
        }

        public event EventHandler<HttpNotificationEventArgs> HttpNotificationReceived;

        void httpChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
        {
            Trace("RAW notification arrived:");
            if (HttpNotificationReceived != null)
                HttpNotificationReceived(sender, e);
        }

        void httpChannel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
        {
            Trace("Toast/Tile notification arrived:");
            foreach (var key in e.Collection.Keys)
            {
                string msg = e.Collection[key];

                Trace(msg);
                Trace("Toast/Tile message: " + msg);
            }
        }

        private bool TryFindChannel()
        {
            bool bRes = false;

            Trace("Getting IsolatedStorage for current Application");
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
            {
                Trace("Checking channel data");
                if (isf.FileExists(fileName))
                {
                    Trace("Channel data exists! Loading...");
                    using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName, FileMode.Open, isf))
                    {
                        using (StreamReader sr = new StreamReader(isfs))
                        {
                            string uri = sr.ReadLine();
                            Trace("Finding channel");
                            httpChannel = HttpNotificationChannel.Find(channelName);

                            if (null != httpChannel)
                            {
                                if (httpChannel.ChannelUri.ToString() == uri)
                                {
                                    Trace("Channel retrieved");
                                    SubscribeToChannelEvents();
                                    SubscribeToService();
                                    bRes = true;
                                }

                                sr.Close();
                            }
                        }
                    }
                }
                else
                    Trace("Channel data not found");
            }

            return bRes;
        }

        private void SaveChannelInfo()
        {
            Trace("Getting IsolatedStorage for current Application");
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
            {
                Trace("Creating data file");
                using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName, FileMode.Create, isf))
                {
                    using (StreamWriter sw = new StreamWriter(isfs))
                    {
                        Trace("Saving channel data...");
                        sw.WriteLine(httpChannel.ChannelUri.ToString());
                        sw.Close();
                        Trace("Saving done");
                    }
                }
            }
        }

        public void Open()
        {
            if (!TryFindChannel())
                DoConnect();
        }

        public void Close()
        {   
            if (httpChannel != null)
                httpChannel.Close();
        }

        public void Dispose()
        {
            Close();
        }
    }
}
